package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.cache.WarehouseCache;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.dto.ExpenseDetailSettlementDTO;
import com.arpa.wms.domain.dto.ReceiptDTO;
import com.arpa.wms.domain.dto.ReceiptEditDTO;
import com.arpa.wms.domain.dto.ReceiptItemEditDTO;
import com.arpa.wms.domain.entity.ExpenseDetail;
import com.arpa.wms.domain.entity.Receipt;
import com.arpa.wms.domain.entity.ReceiptItem;
import com.arpa.wms.domain.enums.ExpenseDetailStatusEnum;
import com.arpa.wms.domain.enums.OrderIdPrefixEnum;
import com.arpa.wms.domain.vo.ReceiptVO;
import com.arpa.wms.mapper.ExpenseDetailMapper;
import com.arpa.wms.mapper.ReceiptMapper;
import com.arpa.wms.service.IReceiptItemService;
import com.arpa.wms.service.IReceiptService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 仓库收款单 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-11-17
 */
@Service
@Log4j2
public class ReceiptServiceImpl extends ServiceImpl<ReceiptMapper, Receipt> implements IReceiptService {

    @Resource
    private ShipmentCache shipmentCache;
    @Resource
    private PartyCache partyCache;
    @Resource
    private WarehouseCache warehouseCache;
    @Resource
    private ExpenseDetailMapper expenseDetailMapper;

    private final IReceiptItemService receiptItemService;

    public ReceiptServiceImpl(IReceiptItemService receiptItemService) {
        this.receiptItemService = receiptItemService;
    }


    /**
     * 保存
     *
     * @param receipts
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(List<Receipt> receipts) {
        saveBatch(receipts);
        receipts.parallelStream().forEach(r -> {
            receiptItemService.saveBatch(r.getReceiptItemList());
        });
        return true;
    }

    /**
     * 根据code更新实体
     *
     * @param entity          结算单
     * @param removeItemCodes 删除的明细code
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Receipt entity, List<String> removeItemCodes) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
        }
        return baseMapper.update(entity, new QueryWrapper<Receipt>().lambda().eq(Receipt::getCode, entity.getCode()).eq(Receipt::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 查询列表
     *
     * @param receiptDTO
     * @return
     */
    @Override
    public List<ReceiptVO> queryList(ReceiptDTO receiptDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(receiptDTO.getSortField())) {
            receiptDTO.setSortField(CommonUtil.camel2Underline(receiptDTO.getSortField()));
        }
        List<ReceiptVO> receiptVOList = baseMapper.queryList(receiptDTO);
        return receiptVOList;
    }

    /**
     * 查询合计,包含总数
     *
     * @param receiptDTO
     * @return
     */
    @Override
    public ReceiptVO queryListSum(ReceiptDTO receiptDTO) {
        ReceiptVO receiptVO = baseMapper.queryListSum(receiptDTO);
        return receiptVO;
    }

    /**
     * 删除结算单
     * 删除收款单的同时，更新收费明细状态
     *
     * @param code
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByCode(String code) {

        Receipt receipt = super.getOne(new QueryWrapper<Receipt>().lambda().eq(Receipt::getCode, code).eq(Receipt::getGroupCode,UserUtil.getBranchCode()));
        if (null == receipt) {
            log.error("收款单删除失败：收款单【{}】找不到", code);
            throw new ServiceException("收款单【" + code + "】找不到");
        }
        if (StrUtil.equalsAny(receipt.getStatus(), "1", "2")) {
            log.error("收款单删除失败：收款单【{}】已提交不能删除", code);
            throw new ServiceException("收款单【" + code + "】已提交不能删除");
        }
        //TODO 增加状态判断
        //获取收款单明细
        List<ReceiptItem> receiveItems = receiptItemService.list(new QueryWrapper<ReceiptItem>().lambda().eq(ReceiptItem::getReceiptCode, code).eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()));
        //获取费用明细
        receiveItems.forEach(r -> {
            //费用信息
            ExpenseDetail expenseDetail = expenseDetailMapper.selectOne(new QueryWrapper<ExpenseDetail>().lambda().eq(ExpenseDetail::getCode, r.getExpenseDetailCode()).eq(ExpenseDetail::getGroupCode,UserUtil.getBranchCode()));
            if (expenseDetail == null) {
                log.error("结算单修改失败：费用信息【{}】找不到", r.getExpenseDetailCode());
                throw new ServiceException("结算单删除失败：费用详情【" + r.getExpenseDetailCode() + "】找不到");
            }
            //还原更新费用信息
            expenseDetailMapper.restoreSettlementStatus(r.getExpenseDetailCode(), r.getThisSettledMoney());

        });
        //删除收款单
        super.remove(new QueryWrapper<Receipt>().lambda().eq(Receipt::getCode, code));
        //删除收款单明细
        receiptItemService.remove(new QueryWrapper<ReceiptItem>().lambda().eq(ReceiptItem::getReceiptCode, code).eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()));


    }


    /**
     * 生成结算单
     * 支持按仓库和货主分组生成多条结算单
     *
     * @param settlementDTO 费用明细code
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlement(final ExpenseDetailSettlementDTO settlementDTO) {

        List<ExpenseDetailSettlementDTO.SettlementItem> items = settlementDTO.getSettlementItems();
        if (IterUtil.isEmpty(items)) {
            log.error("结算费用明细为空");
            throw new ServiceException("结算费用明细为空");
        }

        //费用明细CODE
        List<String> codes = items.stream().map(ExpenseDetailSettlementDTO.SettlementItem::getCode).collect(Collectors.toList());

        //获取费用明细信息
        List<ExpenseDetail> expenseDetails = expenseDetailMapper
                .selectList(new QueryWrapper<ExpenseDetail>().lambda()
                        .in(ExpenseDetail::getCode, codes)
                        .eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode())
                        .ne(ExpenseDetail::getStatus, ExpenseDetailStatusEnum.SETTLED));
        //按照仓库和货主分组
        Map<String, List<ExpenseDetail>> map = expenseDetails.stream().collect(Collectors.groupingBy(e -> fetchGroupKey(e)));

        String userCode = UserUtil.getCode();
        String userName = partyCache.translate(userCode);
        // 遍历生成多条结算单
        List<Receipt> receipts = map.entrySet().parallelStream().map(entry -> {
            Receipt receipt = new Receipt();
            receipt.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SK, "", OrderIdPrefixEnum.WMS,UserUtil.getBranchCode()));
            //默认新建，还需要提交审核
            receipt.setStatus("0");
            receipt.setGroupCode(UserUtil.getBranchCode());
            receipt.setCreatedBy(userCode);
            receipt.setCreatedName(userName);
            receipt.setModifiedBy(userCode);
            receipt.setModifiedName(userName);


            //结算费用明细
            List<ExpenseDetail> list = entry.getValue();
            List<ReceiptItem> receiptItems = list.parallelStream().map(r -> {
                ReceiptItem item = new ReceiptItem();
                BeanUtil.copyProperties(r, item, false);
                item.setCode(IdUtil.simpleUUID());
                item.setCreatedBy(userCode);
                item.setCreatedName(userName);
                item.setModifiedBy(userCode);
                item.setModifiedName(userName);
                item.setReceiptCode(receipt.getCode());
                item.setExpenseDetailCode(r.getCode());
                ExpenseDetailSettlementDTO.SettlementItem settlementItem = items.stream().filter(i -> StrUtil.equals(i.getCode(), r.getCode())).findFirst().orElse(null);
                if (settlementItem == null) {
                    log.error("找不到结算明细，无法设置结算金额");
                    throw new ServiceException("找不到结算明细");
                }
                item.setThisSettledMoney(settlementItem.getThisSettledMoney());
                return item;
            }).collect(Collectors.toList());

            //计算收款金额，并设置货主、仓库
            if (IterUtil.isNotEmpty(receiptItems)) {
                BigDecimal amountCollected = receiptItems.stream().map(ReceiptItem::getThisSettledMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
                receipt.setAmountCollected(amountCollected);
                ReceiptItem item = receiptItems.get(0);
                if (item != null) {
                    receipt.setWarehouseCode(item.getWarehouseCode());
                    receipt.setWarehouseName(warehouseCache.translate(item.getWarehouseCode()));
                    receipt.setShipmentCode(item.getShipmentCode());
                    receipt.setShipmentName(item.getShipmentName());
                }
            }
            receipt.setReceiptItemList(receiptItems);
            return receipt;
        }).collect(Collectors.toList());
        //创建结算单
        save(receipts);
        //更新费用结算金额，收款单审核后更新费用结算状态
        items.forEach(e -> {

            expenseDetailMapper.updateSettlementMoney(e.getCode(), e.getThisSettledMoney(), userCode, partyCache.translate(UserUtil.getCode()));
        });

    }

    /**
     * 更新结算单
     * 关联更改费用明细
     *
     * @param receiptEditDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(ReceiptEditDTO receiptEditDto) {

        //要删除的收款单明细
        if (IterUtil.isNotEmpty(receiptEditDto.getRemoveItemCodes())) {
            List<ReceiptItem> receiveItems = receiptItemService.list(new QueryWrapper<ReceiptItem>().lambda().in(ReceiptItem::getCode, receiptEditDto.getRemoveItemCodes()).eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()));
            if (IterUtil.isNotEmpty(receiveItems)) {
                //获取费用明细
                receiveItems.forEach(r -> {
                    //费用信息
                    ExpenseDetail expenseDetail = expenseDetailMapper.selectOne(new QueryWrapper<ExpenseDetail>().lambda().eq(ExpenseDetail::getCode, r.getExpenseDetailCode()).eq(ExpenseDetail::getGroupCode,UserUtil.getBranchCode()));
                    if (expenseDetail == null) {
                        log.error("结算单修改失败：费用信息【{}】找不到", r.getExpenseDetailCode());
                        throw new ServiceException("结算单删除失败：费用详情【" + r.getExpenseDetailCode() + "】找不到");
                    }
                    //还原更新费用信息
                    expenseDetailMapper.restoreSettlementStatus(r.getExpenseDetailCode(), r.getThisSettledMoney());
                });
                //删除收款单明细
                receiptItemService.remove(new QueryWrapper<ReceiptItem>().lambda().eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()).in(ReceiptItem::getCode, receiptEditDto.getRemoveItemCodes()));
            }
        }

        //更新收款单明细
        List<ReceiptItemEditDTO> receiptItemEditDtos = receiptEditDto.getReceiptItemList();
        if (IterUtil.isNotEmpty(receiptItemEditDtos)) {
            receiptItemEditDtos.forEach(r -> {

                //结算单明细
                final ReceiptItem receiptItem = receiptItemService.getOne(new QueryWrapper<ReceiptItem>().lambda().eq(ReceiptItem::getCode, r.getCode()).eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()));
                if (null == receiptItem) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】找不到", r.getReceiptCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getReceiptCode() + "】结算明细【" + r.getCode() + "】找不到");
                }

                //费用信息
                final ExpenseDetail expenseDetail = expenseDetailMapper.selectOne(new QueryWrapper<ExpenseDetail>().lambda()
                        .eq(ExpenseDetail::getCode, r.getExpenseDetailCode()).eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode()));
                if (expenseDetail == null) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】对应的费用信息【{}】找不到", r.getReceiptCode(), r.getCode(), r.getExpenseDetailCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getReceiptCode() + "】结算明细【" + r.getCode() + "】对应的费用信息【" + r.getExpenseDetailCode() + "】找不到");
                }


                //判断修改后的结算金额是否小于0
                if (r.getNewThisSettledMoney().compareTo(BigDecimal.ZERO) < 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能小于0", r.getReceiptCode(), r.getCode());
                    throw new ServiceException("结算单修改失败：结算单【" + r.getReceiptCode() + "】结算明细【" + r.getCode() + "】结算金额不能小于0");
                }
                //判断修改后的结算金额是否大于费用信息未结算金额
                //计算费用信息未结算金额
                BigDecimal unsettledMoney = expenseDetail.getAmountMoney().subtract(expenseDetail.getSettledMoney()).subtract(expenseDetail.getWaitingSettledMoney()).add(receiptItem.getThisSettledMoney());
                if (r.getNewThisSettledMoney().compareTo(unsettledMoney) > 0) {
                    log.error("结算单修改失败：结算单【{}】结算明细【{}】结算金额不能大于未结算金额【{}】", r.getReceiptCode(), r.getCode(), unsettledMoney);
                    throw new ServiceException("结算单修改失败：结算单【" + r.getReceiptCode() + "】结算明细【" + r.getCode() + "】结算金额不能大于未结算金额【" + unsettledMoney + "】");
                }


                //还原更新费用信息 old =20000，new=2000
                BigDecimal restoreSettledMoney = receiptItem.getThisSettledMoney().subtract(r.getNewThisSettledMoney());
                expenseDetailMapper.restoreSettlementStatus(expenseDetail.getCode(), restoreSettledMoney);


                //更新费用明细
                receiptItemService.update(new UpdateWrapper<ReceiptItem>().lambda()
                        .eq(ReceiptItem::getCode, r.getCode())
                        .eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode())
                        .set(ReceiptItem::getThisSettledMoney, r.getNewThisSettledMoney())
                );

            });
        }




        //更新结算单主表
        update(new UpdateWrapper<Receipt>().lambda()
                .eq(Receipt::getCode, receiptEditDto.getCode())
                .eq(Receipt::getGroupCode, UserUtil.getBranchCode())
                .set(Receipt::getRemarks, receiptEditDto.getRemarks())
                .set(Receipt::getModifiedBy, UserUtil.getCode())
                .set(Receipt::getStatus, receiptEditDto.getStatus())
                //TODO 此处需要更换为后台计算
                .set(Receipt::getAmountCollected, receiptEditDto.getAmountCollected())
                .set(Receipt::getModifiedName, partyCache.translate(UserUtil.getCode()))
        );


        return 0;
    }

    /**
     * 收款单审核
     *
     * @param code          收款单code
     * @param status        审核状态 1：通过。2不通过
     * @param verifyOpinion 审核意见
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approve(String code, int status, String verifyOpinion) {
        Receipt entity = super.getOne(new QueryWrapper<Receipt>().lambda().eq(Receipt::getCode, code).eq(Receipt::getGroupCode, UserUtil.getBranchCode()));
        if (null == entity) {
            log.error("找不到收款单【{}】信息", code);
            throw new ServiceException("找不到收款单【" + code + "】信息");
        }
        if (StrUtil.equalsAny(entity.getStatus(),"2","3")) {
            log.error("收款单【{}】已审核状态【{}】不能重复审核", code,entity.getStatus());
            throw new ServiceException("收款单【" + code + "】已审核，不能重复审核");
        }


        //获取收款单明细
        List<ReceiptItem> receiveItems = receiptItemService.list(new QueryWrapper<ReceiptItem>().lambda().eq(ReceiptItem::getGroupCode,UserUtil.getBranchCode()).eq(ReceiptItem::getReceiptCode, code));
        if (status == 1) {
            entity.setStatus("2");
            receiveItems.forEach(r -> {
                //确认费用结算信息
                expenseDetailMapper.confirmSettlementStatus(r.getExpenseDetailCode(), r.getThisSettledMoney());
            });
        }
        if (status == 2) {
            entity.setStatus("3");
            receiveItems.forEach(r -> {
                //还原更新费用信息
                expenseDetailMapper.restoreSettlementStatus(r.getExpenseDetailCode(), r.getThisSettledMoney());
            });
        }
        String userCode = UserUtil.getCode();
        String userName  = partyCache.translate(userCode);
        entity.setVerifyBy(userCode);
        entity.setVerifyName(userName);
        entity.setModifiedBy(userCode);
        entity.setModifiedName(userName);
        entity.setVerifyOpinion(verifyOpinion);




        return updateById(entity);
    }

    /**
     * 收款单信息
     *
     * @param code 收款单号
     * @return {@link ReceiptVO}
     */
    @Override
    public ReceiptVO getReceipt(String code) {
        Receipt entity = super.getOne(new QueryWrapper<Receipt>().lambda().eq(Receipt::getCode, code).eq(Receipt::getGroupCode, UserUtil.getBranchCode()));
        if (null == entity) {
            log.error("查询收费单【{}】详情失败，找不到收费单信息", code);
            throw new ServiceException("查询收费单【" + code + "】详情失败，找不到收费单信息");
        }
        ReceiptVO receiptVO = new ReceiptVO();
        BeanUtil.copyProperties(entity, receiptVO, false);
        if (StrUtil.isBlank(receiptVO.getWarehouseName()) && StrUtil.isNotBlank(receiptVO.getWarehouseCode())) {
            receiptVO.setWarehouseName(warehouseCache.translate(receiptVO.getWarehouseCode()));
        }
        if (StrUtil.isBlank(receiptVO.getShipmentName()) && StrUtil.isNotBlank(receiptVO.getShipmentCode())) {
            receiptVO.setShipmentName(shipmentCache.translate(receiptVO.getShipmentCode()));
        }
        List<ReceiptItem> receiptItems = receiptItemService.listByReceiptCode(code);
        receiptVO.setReceiptItemList(receiptItems);
        return receiptVO;
    }

    private static String fetchGroupKey(ExpenseDetail expenseDetail) {
        return expenseDetail.getWarehouseCode() + "#" + expenseDetail.getShipmentCode();
    }
}
